package com.yunda.nebula.gateway.sync.http;

import com.alibaba.fastjson.JSONObject;
import com.yunda.nebula.gateway.core.ListenerDto;
import com.yunda.nebula.gateway.core.SyncDataService;
import com.yunda.nebula.gateway.core.entity.ConfigData;
import com.yunda.nebula.gateway.core.entity.FetchAppInfo;
import com.yunda.nebula.gateway.core.enums.DataEventTypeEnum;
import com.yunda.nebula.gateway.core.exception.GateWayException;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * \* User: wei.zhang
 * \* Date: 2020/9/16 15:41
 * \* Description:
 *
 * @author wei.zhang
 * \
 */
@Slf4j
@Data
public class HttpLongPollingTask implements Runnable,HttpTask{



    private AtomicBoolean RUNNING = new AtomicBoolean(true);
    private SyncDataService syncDataService;
    private String url;
    private RestTemplate restTemplate;
    private Integer retryTimes = 3;
    private String appId;
    private DataRefreshFactory factory;
    public HttpLongPollingTask(String url,SyncDataService syncDataService) {
        this.url = url;
        this.syncDataService =  syncDataService;
    }

    @SneakyThrows
    @Override
    public void run() {
        while (RUNNING.get()) {
            for (int time = 1; time <= retryTimes; time++) {
                try {
                    log.info("开始执行获取远端程序任务,appid 为 {}",appId);
                    doLongPolling(appId,url);
                } catch (Exception e) {
                    // print warnning log.
                    if (time < retryTimes) {
                        log.warn("Long polling failed, tried {} times, {} times left, will be suspended for a while! {}",
                                time, retryTimes - time, e.getMessage());
                        TimeUnit.SECONDS.sleep(5);
                        continue;
                    }
                    // print error, then suspended for a while.
                    log.error("Long polling failed, try again after 5 minutes!", e);
                    TimeUnit.SECONDS.sleep(5);
                }
            }
        }
        log.warn("Stop http long polling.");
    }

    private void doLongPolling(String appId,String url) {
//        MultiValueMap<String, String> params = new LinkedMultiValueMap<>(8);
        ConfigData cacheConfig = factory.cacheConfigData(appId);
        String value = String.join(",", cacheConfig.getMd5(), String.valueOf(cacheConfig.getLastModifyTime()));
        ListenerDto listenerDto = new ListenerDto();
        listenerDto.setSign(cacheConfig.getMd5());
        listenerDto.setAppId(cacheConfig.getAppId());
        listenerDto.setTimestamp(cacheConfig.getLastModifyTime());

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpEntity httpEntity = new HttpEntity(listenerDto, headers);


        String listenerUrl = url + "/nebula/gateway/config/listener.do";
        log.debug("request listener configs: [{}]", listenerUrl);
        String groupJson = null;
        try {
            String json = this.restTemplate.postForEntity(listenerUrl, httpEntity, String.class).getBody();
            log.debug("listener result: [{}]", json);
            groupJson = JSONObject.parseObject(json).getString("data");
        } catch (RestClientException e) {
            String message = String.format("listener configs fail, server:[%s], %s", url, e.getMessage());
            throw new GateWayException(message, e);
        }
        if (StringUtils.isNoneBlank(groupJson) && !groupJson.equalsIgnoreCase("null")) {
            // fetch group configuration async.
            FetchAppInfo fetchAppInfo = JSONObject.parseObject(groupJson, FetchAppInfo.class);
            if (fetchAppInfo.getType().equalsIgnoreCase("update")){
                syncDataService.doFetchAppConfig(fetchAppInfo.getAppId(), DataEventTypeEnum.UPDATE);
            }else if (fetchAppInfo.getType().equalsIgnoreCase("delete")){
                syncDataService.doRemoveAppConfig(fetchAppInfo.getAppId(), DataEventTypeEnum.DELETE);
            }else {
                log.info("暂时不支持该操作，appid为 {},操作类型为 {}",fetchAppInfo.getAppId(),fetchAppInfo.getType());
                return;
            }
        }
    }

    @Override
    public void stop() {
        RUNNING.compareAndSet(true,false);
    }
}
